#include "mesh.h"


Mesh::Mesh( )
{
	meshFilename = "";
	setInits( 0.6, 45.0 );

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
	setScenePosition( Vec3f(0.0, 0.0, 0.0), 1.0 );

	setMaterial( );
}


void Mesh::setMaterial( )
{
	GLfloat mat_a[] = { 0.0215,  0.0215, 0.1745, 1.0 };
	GLfloat mat_d[] = { 0.07568, 0.07568, 0.81424,  1.0 };
	GLfloat mat_s[] = { 0.07568, 0.07568, 0.81424, 1.0};
	GLfloat shine[] = { 128.0 * 0.6};

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   mat_a);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   mat_d);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  mat_s);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);

	glColor4f( 0.0f, 0.0f, 1.0f, 1.0f );
}


bool Mesh::openMesh( const char* _filename )
{
	meshFilename = _filename;
	IO::Options	opt;

	mesh.request_face_normals( );
	mesh.request_vertex_normals( );

	if ( IO::read_mesh( mesh, _filename, opt ) )
	{
		// update face and vertex normals     
		if ( ! opt.check( IO::Options::FaceNormal ) )
		  mesh.update_face_normals();

		if ( ! opt.check( IO::Options::VertexNormal ) )
		  mesh.update_vertex_normals();

		// bounding box
		PolyMesh::ConstVertexIter vIt( mesh.vertices_begin() );
		PolyMesh::ConstVertexIter vEnd( mesh.vertices_end() );      

		Vec3f bbMin, bbMax;
		bbMin = bbMax = OpenMesh::vector_cast<Vec3f>( mesh.point(vIt) );

		for (size_t count=0; vIt!=vEnd; ++vIt, ++count)
		{
			bbMin.minimize( OpenMesh::vector_cast<Vec3f>( mesh.point(vIt)) );
			bbMax.maximize( OpenMesh::vector_cast<Vec3f>( mesh.point(vIt)) );
		}

		// set center and radius
		setScenePosition( (bbMin+bbMax)*0.5, (bbMin-bbMax).norm()*0.5 );

		return true; // loading done
  }

  return false;
}

void Mesh::draw( const unsigned int draw_mode )
{  
	 if( !mesh.n_vertices())
		return;

	glMatrixMode( GL_PROJECTION );
	glLoadMatrixd( projectionMatrix );
	glMatrixMode( GL_MODELVIEW );
	glLoadMatrixd( modelviewMatrix );
 
	if( draw_mode == POINTS )
	{
		glDisable(GL_LIGHTING);
		drawMesh( draw_mode );
	}
	else if( draw_mode == WIRE )
	{
		glDisable(GL_LIGHTING);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		drawMesh( draw_mode );
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
	else if( draw_mode == HIDDEN_LINE )
	{
		glDisable(GL_LIGHTING);
		glShadeModel(GL_FLAT);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
		glDepthRange(0.01, 1.0);
		drawMesh(  WIRE );

		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
		glColor4f( 0.0f, 0.0f, 1.0f, 1.0f );
		glDepthRange( 0.0, 1.0 );
		drawMesh( WIRE );

		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);          
	}

	else if( draw_mode == FLAT )
	{
		glEnable(GL_LIGHTING);
		glShadeModel(GL_FLAT);
		drawMesh( draw_mode );
	}
	else if( draw_mode == SMOOTH )
	{
		glEnable(GL_LIGHTING);
		glShadeModel(GL_SMOOTH);
		drawMesh( draw_mode );
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Mesh::drawMesh( const unsigned int _drawmode )
{
	PolyMesh::ConstFaceIter fIt( mesh.faces_begin()), fEnd(mesh.faces_end());
	PolyMesh::ConstFaceVertexIter fvIt;
	
	if( _drawmode == WIRE || _drawmode == FLAT )
	{
		drawFaces( _drawmode );
	}
	else if( _drawmode == SMOOTH )
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3, GL_FLOAT, 0, mesh.points());
		glEnableClientState(GL_NORMAL_ARRAY);
		glNormalPointer(GL_FLOAT, 0, mesh.vertex_normals());

		drawFaces( _drawmode );

		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
	}  
	else if( _drawmode == POINTS ) 
	{
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3, GL_FLOAT, 0, mesh.points());
		glDrawArrays( GL_POINTS, 0, mesh.n_vertices() );
		glDisableClientState(GL_VERTEX_ARRAY);
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Mesh::drawFaces( const unsigned int _drawmode )
{
	PolyMesh::ConstFaceIter fIt( mesh.faces_begin()), fEnd(mesh.faces_end());
	PolyMesh::ConstFaceVertexIter fvIt;

	for( ; fIt !=fEnd; ++fIt )
	{
		glBegin( GL_POLYGON );

		if(  _drawmode == FLAT )
			glNormal3fv( &mesh.normal(fIt)[0] );

		fvIt = mesh.cfv_iter(fIt.handle());
		
		if( _drawmode == WIRE || _drawmode == FLAT )
		{
			for( ; fvIt != false; ++fvIt )
				glVertex3fv( &mesh.point(fvIt)[0] );
		}
		else
		{
			for( ; fvIt != false; ++fvIt )
				glArrayElement(fvIt.handle().idx());
		}

		glEnd( );
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~